/*
 * Copyright 2023-2024, Haiku, Inc. All rights reserved.
 * Distributed under the terms of the MIT License.
 *
 * 龙芯3A6000 EXTIOI中断控制器驱动
 * 参考Linux的drivers/irqchip/irq-loongson-extioi.c实现
 */

#include "loongson_extioi.h"

#include <KernelExport.h>
#include <SupportDefs.h>
#include <arch/cpu.h>
#include <arch/loongarch64/arch_int.h>
#include <util/kernel_cpp.h>

#define TRACE_LOONGSON_EXTIOI
#ifdef TRACE_LOONGSON_EXTIOI
#define TRACE(x...) dprintf("loongson_extioi: " x)
#else
#define TRACE(x...)
#endif

#define ERROR(x...) dprintf("loongson_extioi: " x)

// 全局中断控制器实例
static loongson_extioi *sEXTIOI = NULL;

// 定时器相关常量
#define LOONGSON_TIMER_IRQ 0                   // 定时器中断号
#define LOONGSON_TIMER_NODE 0                  // 定时器节点
#define LOONGSON_TIMER_IP LOONGSON_EXTIOI_IP_7 // 定时器优先级

// 定时器寄存器偏移
#define LOONGSON_TIMER_CFG 0x0    // 定时器配置寄存器
#define LOONGSON_TIMER_TICK 0x8   // 定时器计数寄存器
#define LOONGSON_TIMER_COUNT 0x10 // 定时器计数值寄存器

// 定时器配置位
#define LOONGSON_TIMER_CFG_EN (1 << 0)       // 定时器使能
#define LOONGSON_TIMER_CFG_PERIODIC (1 << 1) // 周期性模式
#define LOONGSON_TIMER_CFG_SINGLE (0 << 1)   // 单次模式

// 定时器基地址（需要在初始化时设置）
static addr_t sTimerBase = 0;

// 寄存器读写函数
static inline uint32
read_extioi_reg(addr_t base, uint32 reg)
{
    return *(volatile uint32 *)(base + reg);
}

static inline void
write_extioi_reg(addr_t base, uint32 reg, uint32 val)
{
    *(volatile uint32 *)(base + reg) = val;
}

// 定时器寄存器读写函数
static inline uint64
read_timer_reg(uint32 reg)
{
    return *(volatile uint64 *)(sTimerBase + reg);
}

static inline void
write_timer_reg(uint32 reg, uint64 val)
{
    *(volatile uint64 *)(sTimerBase + reg) = val;
}

// 中断控制器初始化
status_t
loongson_extioi_init(addr_t base_addr, uint32 irq_base)
{
    TRACE("Initializing Loongson EXTIOI at %#" B_PRIxADDR "\n", base_addr);

    // 分配中断控制器结构体
    sEXTIOI = new (std::nothrow) loongson_extioi;
    if (sEXTIOI == NULL)
    {
        ERROR("Failed to allocate loongson_extioi structure\n");
        return B_NO_MEMORY;
    }

    // 初始化结构体
    memset(sEXTIOI, 0, sizeof(loongson_extioi));
    sEXTIOI->base_addr = base_addr;
    sEXTIOI->irq_base = irq_base;
    sEXTIOI->num_irqs = LOONGSON_EXTIOI_MAX_IRQS;
    sEXTIOI->num_nodes = LOONGSON_EXTIOI_MAX_NODES;

    // 禁用所有中断
    for (uint32 i = 0; i < sEXTIOI->num_irqs / LOONGSON_EXTIOI_IRQ_PER_REG; i++)
    {
        write_extioi_reg(base_addr, LOONGSON_EXTIOI_ENABLE_BASE + i * 4, 0);
        sEXTIOI->irq_enable[i] = 0;
    }

    // 初始化中断映射
    for (uint32 i = 0; i < sEXTIOI->num_irqs; i++)
    {
        uint32 reg_index = i / 32;
        uint32 bit_index = i % 32;
        uint32 shift = bit_index * 4;

        // 默认映射到节点0
        uint32 node_val = read_extioi_reg(base_addr, LOONGSON_EXTIOI_NODEMAP_BASE + reg_index * 4);
        node_val &= ~(0xF << shift);
        node_val |= (0 << shift);
        write_extioi_reg(base_addr, LOONGSON_EXTIOI_NODEMAP_BASE + reg_index * 4, node_val);

        // 默认优先级为IP_1
        uint32 ip_val = read_extioi_reg(base_addr, LOONGSON_EXTIOI_IPMAP_BASE + reg_index * 4);
        ip_val &= ~(0xF << shift);
        ip_val |= (LOONGSON_EXTIOI_IP_1 << shift);
        write_extioi_reg(base_addr, LOONGSON_EXTIOI_IPMAP_BASE + reg_index * 4, ip_val);
    }

    TRACE("Loongson EXTIOI initialized successfully\n");
    return B_OK;
}

// 中断控制器卸载
void loongson_extioi_uninit(void)
{
    if (sEXTIOI != NULL)
    {
        // 禁用所有中断
        for (uint32 i = 0; i < sEXTIOI->num_irqs / LOONGSON_EXTIOI_IRQ_PER_REG; i++)
        {
            write_extioi_reg(sEXTIOI->base_addr, LOONGSON_EXTIOI_ENABLE_BASE + i * 4, 0);
        }

        delete sEXTIOI;
        sEXTIOI = NULL;
    }
}

// 启用中断
status_t
loongson_extioi_enable_interrupt(uint32 irq)
{
    if (sEXTIOI == NULL)
        return B_NO_INIT;

    if (irq >= sEXTIOI->num_irqs)
        return B_BAD_VALUE;

    uint32 reg_index = irq / 32;
    uint32 bit_index = irq % 32;

    // 设置中断使能位
    sEXTIOI->irq_enable[reg_index] |= (1 << bit_index);

    // 更新硬件寄存器
    write_extioi_reg(sEXTIOI->base_addr,
                     LOONGSON_EXTIOI_ENABLE_BASE + reg_index * 4,
                     sEXTIOI->irq_enable[reg_index]);

    TRACE("Enabled interrupt %lu\n", irq);
    return B_OK;
}

// 禁用中断
status_t
loongson_extioi_disable_interrupt(uint32 irq)
{
    if (sEXTIOI == NULL)
        return B_NO_INIT;

    if (irq >= sEXTIOI->num_irqs)
        return B_BAD_VALUE;

    uint32 reg_index = irq / 32;
    uint32 bit_index = irq % 32;

    // 清除中断使能位
    sEXTIOI->irq_enable[reg_index] &= ~(1 << bit_index);

    // 更新硬件寄存器
    write_extioi_reg(sEXTIOI->base_addr,
                     LOONGSON_EXTIOI_ENABLE_BASE + reg_index * 4,
                     sEXTIOI->irq_enable[reg_index]);

    TRACE("Disabled interrupt %lu\n", irq);
    return B_OK;
}

// 配置中断
status_t
loongson_extioi_configure_interrupt(uint32 irq, uint32 node, uint32 ip)
{
    if (sEXTIOI == NULL)
        return B_NO_INIT;

    if (irq >= sEXTIOI->num_irqs || node >= sEXTIOI->num_nodes || ip > LOONGSON_EXTIOI_IP_7)
        return B_BAD_VALUE;

    uint32 reg_index = irq / 32;
    uint32 bit_index = irq % 32;
    uint32 shift = bit_index * 4;

    // 配置节点映射
    uint32 node_val = read_extioi_reg(sEXTIOI->base_addr, LOONGSON_EXTIOI_NODEMAP_BASE + reg_index * 4);
    node_val &= ~(0xF << shift);
    node_val |= (node << shift);
    write_extioi_reg(sEXTIOI->base_addr, LOONGSON_EXTIOI_NODEMAP_BASE + reg_index * 4, node_val);

    // 配置优先级映射
    uint32 ip_val = read_extioi_reg(sEXTIOI->base_addr, LOONGSON_EXTIOI_IPMAP_BASE + reg_index * 4);
    ip_val &= ~(0xF << shift);
    ip_val |= (ip << shift);
    write_extioi_reg(sEXTIOI->base_addr, LOONGSON_EXTIOI_IPMAP_BASE + reg_index * 4, ip_val);

    TRACE("Configured interrupt %lu with node %lu and IP %lu\n", irq, node, ip);
    return B_OK;
}

// 中断处理函数
int32 loongson_extioi_handle_interrupt(void *data)
{
    if (sEXTIOI == NULL)
        return B_HANDLED_INTERRUPT;

    // 读取中断状态寄存器
    for (uint32 i = 0; i < sEXTIOI->num_irqs / LOONGSON_EXTIOI_IRQ_PER_REG; i++)
    {
        uint32 status = read_extioi_reg(sEXTIOI->base_addr, LOONGSON_EXTIOI_STATUS_BASE + i * 4);
        uint32 enabled = sEXTIOI->irq_enable[i];
        uint32 active = status & enabled;

        if (active)
        {
            // 处理每个活动中断
            for (uint32 bit = 0; bit < 32; bit++)
            {
                if (active & (1 << bit))
                {
                    uint32 irq = i * 32 + bit;
                    TRACE("Handling interrupt %lu\n", irq);

                    // 清除中断状态
                    write_extioi_reg(sEXTIOI->base_addr, LOONGSON_EXTIOI_STATUS_BASE + i * 4, 1 << bit);

                    // 调用中断处理程序
                    int_io_interrupt_handler(irq + sEXTIOI->irq_base);
                }
            }
        }
    }

    return B_HANDLED_INTERRUPT;
}

// 定时器初始化
status_t
loongson_extioi_timer_init(void)
{
    if (sEXTIOI == NULL)
        return B_NO_INIT;

    // 设置定时器基地址（这里假设定时器基地址是EXTIOI基地址加上一个偏移）
    // 实际实现中，这个地址应该从设备树或其他配置中获取
    sTimerBase = sEXTIOI->base_addr + 0x1000; // 假设偏移为0x1000

    // 配置定时器中断
    loongson_extioi_configure_interrupt(LOONGSON_TIMER_IRQ, LOONGSON_TIMER_NODE, LOONGSON_TIMER_IP);
    loongson_extioi_enable_interrupt(LOONGSON_TIMER_IRQ);

    // 初始化定时器配置
    write_timer_reg(LOONGSON_TIMER_CFG, 0);   // 先禁用定时器
    write_timer_reg(LOONGSON_TIMER_COUNT, 0); // 清零计数器

    TRACE("Timer initialized\n");
    return B_OK;
}

// 设置周期性定时器
void loongson_extioi_timer_set_periodic(bigtime_t period)
{
    // 将微秒转换为定时器计数值
    // 假设定时器频率为1MHz，即1个计数为1微秒
    uint64 count = period;

    // 设置定时器为周期性模式
    write_timer_reg(LOONGSON_TIMER_COUNT, count);
    write_timer_reg(LOONGSON_TIMER_CFG, LOONGSON_TIMER_CFG_EN | LOONGSON_TIMER_CFG_PERIODIC);

    TRACE("Set periodic timer with period %lld us\n", period);
}

// 设置单次定时器事件
void loongson_extioi_timer_set_event(bigtime_t timeout)
{
    // 将微秒转换为定时器计数值
    uint64 count = timeout;

    // 设置定时器为单次模式
    write_timer_reg(LOONGSON_TIMER_COUNT, count);
    write_timer_reg(LOONGSON_TIMER_CFG, LOONGSON_TIMER_CFG_EN | LOONGSON_TIMER_CFG_SINGLE);

    TRACE("Set timer event with timeout %lld us\n", timeout);
}

// 清除定时器事件
void loongson_extioi_timer_clear_event(void)
{
    // 禁用定时器
    write_timer_reg(LOONGSON_TIMER_CFG, 0);
    write_timer_reg(LOONGSON_TIMER_COUNT, 0);

    TRACE("Cleared timer event\n");
}

// 获取当前时间
bigtime_t
loongson_extioi_timer_get_time(void)
{
    // 读取当前计数值
    uint64 tick = read_timer_reg(LOONGSON_TIMER_TICK);

    // 假设定时器频率为1MHz，即1个计数为1微秒
    return (bigtime_t)tick;
}